package getools4me;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class RequestHandler extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6178121227982755577L;

	/**
	 * the list to hold 10 string class, to generate the dynamic array of method parameters type
	 */
	private static List<Class<? extends Object>> list_parameterTypes = new ArrayList<Class<? extends Object>>();
	
	/**
	 * initial the parameterTypes list, 
	 * max parameter numbers are 10;
	 */
	static{
		for(int i=0;i<10;i++){
			list_parameterTypes.add(String.class);
		}
	}
	
	/**
	 * the byte array to hold the error, so those errors can be output to web page
	 */
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	
	
	/**
	 * redirect the system standard error to byte array, so the error can be saved
	 */
	@Override
	public void init() throws ServletException {
		PrintStream ps_error = new PrintStream(baos);
		System.setErr(ps_error);
	}
	
	
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// the result of execute method
		Object result = null;
		// clear the error of last time
		baos.reset();
		
		// get request path
		String path = req.getRequestURI();

		// 1. if the path end with ".class"
		while (path.endsWith(".class")) {
			// get class
			String className = path.substring(1).replace(".class", "");
			Class<? extends Object> clazz = getClass(className);
			if(clazz==null) break;
			// get method and parameters
			String methodName = req.getParameter("method");
			if(methodName==null) break;
			Method md = null;
			String[] parameters = null;
			int paraSize = 0;
			@SuppressWarnings("unchecked")
			Enumeration<String> names = req.getParameterNames();
			while (names.hasMoreElements()) {
				String name = names.nextElement();
				if(name.matches("\\$\\d+")){
					paraSize++;
				}
			}
			parameters = new String[paraSize];
			for(int i=0;i<paraSize;i++){
				parameters[i] = req.getParameter("$"+i);
			}
			md = getMethod(clazz, methodName, paraSize);
			if(md==null) break;
			// get result
			result = executeMethod(clazz, md, parameters);
			if(result==null) break;
			resp.setContentType("text/html");
			resp.getOutputStream().write(result.toString().getBytes());
			
			return;
		}

		// output the error
		if(baos.size()>0){
			resp.setContentType("text/plain");
			resp.getOutputStream().write(baos.toByteArray());
			return;
		}
		
		// 2. find in the resource memory
//		DownloadableFile file = ResourceMemory.getResource(req.getRequestURI());
//		if (file == null) {
//			resp.sendError(404,
//					"Oh, sorry dude, this url \"" + req.getRequestURI()
//							+ "\" can not be found.");
//		} else {
//			resp.setContentType(file.getMimeType());
//			resp.getOutputStream().write(file.getContent());
//		}

	}

	public static void main(String[] args) {
		
		Class<? extends Object> clazz = getClass("domain.ResourceMemory");
		
		if(clazz!=null){
			Method md = getMethod(getClass("domain.ResourceMemory"), "getResouceListAsJson", 0);
			System.out.println(executeMethod(clazz, md, null));
		}
		
		System.out.println("$1343".matches("\\$\\d+"));
		
	}
	
	
	public static Class<? extends Object> getClass(String className){
		Class<? extends Object> clazz = null;
		try {
			clazz = Class.forName(className);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return clazz;
	}
	
	/**
	 * all the method parameters are String, by default
	 * @param className
	 * @param method
	 * @param paraSize
	 * @return
	 */
	public static Method getMethod(Class<? extends Object> clazz, String method, int paraSize) {
		Method md = null;
		try {
			md = clazz.getMethod(method, list_parameterTypes.subList(0, paraSize).toArray(new Class[]{}));
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		return md;
	}
	
	public static Object executeMethod(Class<? extends Object> clazz, Method md, String[] args){
		Object result = null;
		try {
			result = md.invoke(clazz, (Object[])args);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return result;
	}
}
